use std::{collections::BTreeMap, io::Write, path::Path};
use std::fs::OpenOptions;
use crate::{ExportAction, Info, SqlConfig};

const TEMPLATE: &str = include_str!("index.html");
pub(crate) struct Html;

impl ExportAction for Html {
    fn run(
        config: &crate::SqlConfig,
        groups: &std::collections::BTreeMap<&String, Vec<&crate::Info>>,
    ) -> Result<String, crate::RunError> {
        let output = get_output(config);
        let path = Path::new(&output);
        let mut file = OpenOptions::new()
            .write(true)
            .create(true)
            .truncate(true)
            .open(path).unwrap();
        let str = write(groups);
        let str = TEMPLATE.replace("{}",&str).replace("{name}",&config.database);
        file.write_all(str.as_bytes()).unwrap();
        Ok(output)
    }
}

fn write(groups: &BTreeMap<&String, Vec<&Info>>) -> String {
    groups
        .iter()
        .enumerate()
        .fold(String::new(), |mut str, (index, (name, list))| {
            let title = draw_title(index.to_string(), name);
            let table = draw_table(list);
            str = format!("{str}{title}{table}");
            str
        })
}

fn draw_title<T, U>(index: T, name: U) -> String
where
    T: AsRef<str>,
    U: AsRef<str>,
{
    let index = index.as_ref();
    let name = name.as_ref();
    format!("<h2>{index}. {name}</h2>")
}

fn draw_table(list: &[&Info]) -> String {
    let head = draw_table_head();
    let body = list.iter().fold(String::new(),|mut str,info| {
        let Info {
            ordinal_position,
            data_type,
            is_nullable,
            column_name,
            column_comment,
            ..
        } = info;
        let column_comment = column_comment.as_ref().map_or("", |v| v.as_str());
        str = format!("{str}<tr><td>{ordinal_position}</td><td>{column_name}</td><td>{data_type}</td><td>{is_nullable}</td><td>{column_comment}</td></tr>");
        str
    });
    format!(r#"<table>{head}<tbody>{body}</tbody></table>"#)
}

fn draw_table_head() -> &'static str {
    "<thead><tr><th>序号</th><th>列名</th><th>数据类型</th><th>为Null</th><th>备注</th></tr></thead>"
}

fn get_output(config: &SqlConfig) -> String {
    let name = format!("{}.html", config.database);
    config
        .output
        .as_ref()
        .map(|v| {
            if v.ends_with(".html") {
                v.clone()
            } else {
                format!("{}.html", v)
            }
        })
        .unwrap_or(name)
}
